﻿#define txtLogAll
#undef DEBUG
//是否写日志
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Threading;
using System.Xml.Serialization;
using TSharp.Core.Caching;
using System.Web.Caching;

namespace TSharp.Core.Util.Config
{
    /// <summary>
    /// 序列化基类  by Tjb 2007/06
    /// T 类型不能创建多态的多个实例
    /// <para>
    /// 优化和修正，增加配置文件备份 2008/9/18
    /// </para>
    /// 	<para>
    /// 修改：获取实例时加锁
    /// </para>
    /// 	<para>
    /// 修改： by tjb @ 2008/10/24
    /// SaveAs(T Tobj, string FileFullName, bool BackupOnError) 方法增加线程锁定。防止多线程冲突
    /// </para>
    /// 	<para>
    /// 修改： by tjb @ 2008/12/16
    /// 优化线程锁，以及优化缓存key的获取方式，使得typeof(T)运算只运行一次
    /// </para>
    /// </summary>
    /// <typeparam name="T">T 必须有无参数的构造函数</typeparam>
    /// <author>
    /// tangjingbo
    /// </author>
    /// <remarks>
    /// tangjingbo at 2009-10-19 12:19
    /// </remarks>
    [Serializable]
    public abstract class SystemConfig<T> where T : SystemConfig<T>, new()
    {
        #region Ignores

        [NonSerialized, XmlIgnore, SoapIgnore]
        private static readonly Type _curType = typeof(T);
        /// <summary>
        /// 
        /// </summary>
        [NonSerialized, XmlIgnore, SoapIgnore]
        public static readonly string cacheKEY = "_cfg$tjb$" + _curType.FullName;

        [NonSerialized, XmlIgnore, SoapIgnore]
        private readonly string ___file =
            Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
            "App_Data", _curType.FullName + ".config");

        /// <summary>
        /// Gets the type of the format.
        /// </summary>
        /// <value>
        /// The type of the format.
        /// </value>
        [XmlIgnore, SoapIgnore]
        public virtual FormatType FormatType
        {
            get { return FormatType.Xml; }
        }

        /// <summary>
        /// Gets the file path.
        /// </summary>
        [XmlIgnore, SoapIgnore]
        public virtual string FilePath
        {
            get { return ___file; }
        }

        /// <summary>
        /// Gets the defalut.
        /// </summary>
        [XmlIgnore, SoapIgnore]
        public virtual Func<T> Defalut
        {
            get { return () => new T(); }
        }

        #endregion

        /// <summary>
        /// 获取配置对象
        /// </summary>
        public static T Instance
        {
            get
            {
                return CacheUtil.GetOrAdd(cacheKEY, key =>
                                                        {
                                                            var factory = new T();
                                                            T t;
                                                            Exception error = null;
                                                            t = GetFromFile(factory.FilePath, factory.FormatType,
                                                                            ref error);
                                                            if (t == null)
                                                                t = factory.Defalut();
                                                            return t;
                                                        }, TimeSpan.FromDays(7), (k, v) => new CacheDependency(v.FilePath), null);
            }
        }

        #region 获取

        /// <summary>
        /// 从文件读取配置
        /// </summary>
        /// <param name="configFileFullName">Full name of the config file.</param>
        /// <param name="serializeType">Type of the serialize.</param>
        /// <param name="error">The error.</param>
        /// <returns></returns>
        public static T GetFromFile(string configFileFullName, FormatType serializeType, ref Exception error)
        {
            T t = default(T);

            #region  open the stream...

            if (File.Exists(configFileFullName))
                try
                {
                    using (
                        var fs = new FileStream(configFileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
                        )
                    // open the stream...
                    {
                        switch (serializeType)
                        {
                            case FormatType.Binary:
                                t = new BinaryFormatter().Deserialize(fs) as T;
                                break;
                            case FormatType.SoapXml:
                                t = new SoapFormatter().Deserialize(fs) as T;
                                break;
                            case FormatType.Xml:
                                t = new XmlSerializer(_curType).Deserialize(fs) as T;
                                break;
                            default:
                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    error =
                        new Exception(
                            string.Format("反序列化时发生异常。filePath:{0},Class Type:{1}", configFileFullName, _curType.FullName),
                            ex);
                }
            else
                error = new Exception(string.Format("文件不存在：{0}", configFileFullName));

            #endregion

            return t;
        }

        #endregion

        #region 保存

        /// <summary>
        /// Saves this instance.
        /// </summary>
        /// <returns></returns>
        public bool Save()
        {
            Exception error = null;
            return Save((T)this, ref error);
        }

        /// <summary>
        /// Saves the specified obj.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="error">The error.</param>
        /// <returns></returns>
        public static bool Save(T obj, ref Exception error)
        {
            if (obj == null) return false;
            return SaveToFile(obj, obj.FilePath, true, obj.FormatType, ref error);
        }

        /// <summary>
        /// 将一个实例对象保存到FileFullName指定的配置文件
        /// </summary>
        /// <param name="obj">The tobj.</param>
        /// <param name="fileFullName">Full name of the file.</param>
        /// <param name="backupOnError">if set to <c>true</c> [backup on error].</param>
        /// <param name="serializeType">Type of the serialize.</param>
        /// <param name="error">The error.</param>
        /// <returns></returns>
        public static bool SaveToFile(T obj, string fileFullName, bool backupOnError, FormatType serializeType,
                                      ref Exception error)
        {

            {
                string backupFilePath = null;
                if (backupOnError)
                {
                    #region backup

                    try
                    {
                        var fi = new FileInfo(fileFullName);
                        //如果存在先备份
                        if (fi.Exists)
                        {
                            backupFilePath = string.Format("{0}.Save{1}.bak", fi.FullName,
                                                           DateTime.Now.ToString("yyMMddHHssfff"));
                            fi.CopyTo(backupFilePath, true);
                        }
                        else if (!fi.Directory.Exists)
                        {
                            //目录不存在创建目录
                            fi.Directory.Create();
                        }
                    }
                    catch (Exception ex)
                    {
                        error = new Exception("保存前备份时异常。", ex);
                        return false;
                    }

                    #endregion
                }

                try
                {
                    #region  serialize it...
                    lock (_curType)
                        using (var fs = new FileStream(fileFullName, FileMode.Create, FileAccess.Write, FileShare.Read))
                        {
                            switch (serializeType)
                            {
                                case FormatType.Binary:
                                    new BinaryFormatter().Serialize(fs, obj);
                                    break;
                                case FormatType.SoapXml:
                                    new SoapFormatter().Serialize(fs, obj);
                                    break;
                                case FormatType.Xml:
                                    new XmlSerializer(_curType).Serialize(fs, obj);
                                    break;
                                default:
                                    break;
                            }
                        }

                    #endregion

                    #region 成功则删除当前备份

                    if (backupOnError && !string.IsNullOrEmpty(backupFilePath))
                        try
                        {
                            File.Delete(backupFilePath);
                        }
                        catch (Exception ex)
                        {
                            error = new Exception(string.Format("保存配置文件成功，删除备份文件出错:'{0}'", backupFilePath), ex);
                        }

                    #endregion

                    return true;
                }
                catch (UnauthorizedAccessException ioex)
                {
                    error =
                        new Exception(
                            string.Format("保存配置文件时发生IO错误！[TheadID:{0},ThreadName:{1}]",
                                          Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name), ioex);
                    return false;
                }
                catch (Exception ex)
                {
                    error =
                        new Exception(string.Format("保存配置文件时发生错误！[TheadID:{0},ThreadName:{1}]",
                                          Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name), ex);
                    return false;
                }
            }
        }

        #endregion
    }
}